"""
The Scary Door (Working title), rescue the cute dudes to beat your opponents.
Copyright (C) 2005, 2006, 2007  Nathan Lanyon

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""

import pygame, sys, os, random

from twisted.spread import pb
from twisted.internet import reactor
from twisted.cred import credentials
from pygame.locals import *
from player import player_c
from map import map_c
from anim import anim_c
from exit import exit_c
from entrance import entrance_c
from mouse import mouse_c
#from mouse import mouseArea
from gui import actionMenu_c, mapMover_c, gameArea_c, gui_c, blankArea_c, mapBrowser_c

#from actionMenu import actionMenu_c

import maplist

from loadData import *

class game_c(pb.Referenceable):
    def __init__(self, programTitle):
    
        pygame.mixer.pre_init(44100, -16, 2, 4096)
        pygame.init()
        
        #try to initialise sound, else turn it off
        self.soundEnabled = True
        try:
            pygame.mixer.init(7092)
        except:
            print "** Sound could not be initialised.  If you're using PulseAudio it could be your sdl version being set to alsa and another program using the sound."
            self.soundEnabled = False
        
        pygame.mouse.set_visible(0)
        
        """ DISPLAY INITIALISATION """
        #set up a window
        self.windowSize=(800,600)
        self.window = pygame.display.set_mode(self.windowSize, HWSURFACE|DOUBLEBUF)
        #set the window caption
        pygame.display.set_caption(programTitle)
        #grab the main drawing surface
        self.screen = pygame.display.get_surface()
        #grab a font
        self.font = pygame.font.Font("data/liberation-fonts-0.2/LiberationSans-Regular.ttf", 10 )
        
        """ GAME VARIABLES """
        self.restartGame = False
        self.endGame = False
        self.perspective = None
        self.factory = None
        self.mouse = mouse_c()

        #frame counter, to sync the games
        self.framesDrawn = 0
        #A list of areas on the main playfield to be redrawn
        self.redrawArea=[]
        #the rect containing all creeps
        self.drawRect=[]
        self.drawBuffer=6            #area around the undraw rect for the background redraw
        #the actual view of the play area
        self.viewPortalSize=(800, 340)
        #amount of frames to redraw to refresh the whole screen, probably just to catch the double buffer
        self.redrawAll=2
        self.username = str(random.random())
        self.clientNumber = 0
        
        self.objects = []
        self.players = []

        """ LOAD MEDIA """
        self.img={}
        self.anim={}
        self.sound={}
        loadGameData(self)

        """ STUPID MENU STUFF """ 
        self.menuItemWidth = 32        
        #menuActions=[-1, -1, "climber", "floater", "bomber", "blocker", "builder", "basher", "miner", "digger", -1, -1]  #maybe later
        self.menuActions = [-1, -1, 7, 8, 3, 9, 5, 2, 11, 10, 12, 69] 
        
        """ NEED TO BE CATEGORISED, can possibly go? """
        self.spriteSize = (32,32)
        self.mainGameLoops=-1        #loops to run before exit.  Leave at -1 for infinite
        self.scoreHeight = 18
        
        #a list of blockers, so that each dude isn't tested against every other dude.  should prolly be a list of lists for flexability
        self.blockers=[]
        
        """ PLAYER SETUP """
        #Set up current player, this should be done on server or single player game setup in future
        # actually would prefer to do it on the fly when people add and leave the game at the server level?
        # have server assign player number to players as they join or leave
        
        self.crntPlayer = player_c("1", number=len(self.players))
        self.players += [self.crntPlayer]
        self.players += [player_c("2", number=len(self.players))]
        
        """ INIT THE MAP """
        self.maps = maplist.getMapList(self)
        self.crntMap = self.maps[1]
        self.crntMap.reset() # why am i doing this here and not in the map constructer?
        
        """
        if mouse over and mouse down, call click?  no, call test func,  it can call mouseOver or mouseClick as it sees fit, fuck it
        every frame pass the mouse redraw area to every gui object as a redraw area and it can fuck it off if it doesn't want it
        """
        self.mover = mapMover_c(pygame.Rect(400, 340, 350, 250), self.crntMap.mapSurface, self)
        self.actionMenu = actionMenu_c(pygame.Rect(0,340,390,350), self.img["menuSurface"], self)
        self.gameArea = gameArea_c(pygame.Rect(0, 0, 800, 340), self)
        self.mapBrowser = mapBrowser_c(pygame.Rect(0, 0, 200, 100), self)

        self.blank1 = blankArea_c(pygame.Rect(0, 388, 350, 200), self, "Default Item")
        
        #hacka hacka hacka
        self.guiItems = [self.mover, self.actionMenu, self.gameArea, self.blank1, self.mapBrowser]


        """ START YOUR ENGINE """
        #self.connect()
        self.startWithoutConnect()

    def initVars(self):
        self.blockers = []
        self.objects = []

    def drawScores(self, a, b):
        pass
    
    def remote_print(self, message):
        print message

    #attempts to connect to a game server, currently only checks localhost
    def connect(self):
        self.factory = pb.PBClientFactory()
        reactor.connectTCP("localhost", 8800, self.factory, timeout=5)
        dfr = self.factory.getRootObject()

        dfr.addCallback(self.login)
        dfr.addErrback(self.connectFailed)
        reactor.run()
        
    def login(self, thing):
        def1 = self.factory.login(credentials.UsernamePassword("anon", "anon"), client=self)
        def1.addCallback(self.connected)
        #def1.addErrback(self.loginFailed)

    #starts the game loop without attempting to connect to a game server
    def startWithoutConnect(self):
        reactor.callLater(0.03, self.main)
        reactor.run()
        
    def connectFailed(self, error):
        print "Connection Failed", error
        reactor.stop()
        
    def loginFailed(self, error):
        print "Login Failed"
        reactor.stop()

    #set up the perspective Var which is a pointer to the user object on the server
    def connected(self, perspective):
        self.perspective = perspective
        
        #insert functions that need to be called so they can call themselves periodically here
        self.main()
    
    #will this need to change if we're not connected?
    def shutDown(self):
        reactor.stop()

    #updates the mouse class,  should the mouse class just handle this itself?
    def checkEvents(self, events):
        for event in events:
            if event.type == QUIT:
                self.endGame=True
            elif event.type == MOUSEMOTION:
                self.mouse.setPos((event.pos[0], event.pos[1]))
            elif event.type == MOUSEBUTTONDOWN:
                self.mouse.setButton(event.button-1,1)
            elif event.type == MOUSEBUTTONUP:
                self.mouse.setButton(event.button-1,0)

    def playSound(self, soundName):
        if self.soundEnabled:
            self.sound[soundName].play()

    #i'm not sure this is the best way to go, should probably just have a separate list for each type of creep.  this will assist in creeps that interact with each other
    #creep[type][creepTypeAmt]=a creep of this type etc
    #this kind of check is pretty heavy, would be nice to find some other way to do it
    def addBlocker(self, blocker):
        self.blockers += [blocker]

    def remBlocker(self, blocker):
        i=0
        while i <= len(self.blockers):
            if blocker == self.blockers[i]:
                del self.blockers[i]
                break
            i+=1

    def restart(self):
        self.__init__("")

    #writes some text to the main surface, it would be a lot cleaner to pass back a surface and get the caller to handle the drawing
    def writeText(self, text, color):
        s = self.font.render(text, True, color)
        return s

    #main func, might want to split up the timings a bit, for eg, check events less often than draw or something?
    def main(self):
        
        #clear up the draws on the last frame before processing or moving anything
        for guiItem in self.guiItems:
            guiItem.checkMouse(self.mouse)
            #guiItem.checkArea(self.mouse.area)
            guiItem.checkDirty()
        
        #update the mouse etc
        self.checkEvents(pygame.event.get())

        #set the clip area to the whole screen, not sure why i need to do this
        #clip area is the drawable area, should not need to reset it to everything
        self.screen.set_clip(0, 0, self.windowSize[0], self.windowSize[1])
        
        self.screen.blit(self.img["mouseSurface"], (self.mouse.pos[0]-16, self.mouse.pos[1]-16))

        pygame.display.flip()
        
        #if endGame has been set, call the shutdown function
        if self.endGame==True:
            self.shutDown()

        if self.restartGame==True:
            self.restart()

        #come back here at a rate of approx 30fps
        reactor.callLater(0.03, self.main)

if __name__=="__main__":
    try:
        import psyco
        psyco.full()
        print "Psyco enabled"
    except ImportError:
        print "** Could not init Psyco: don't bother trying to install it if you're on 64bit"

    game = game_c('The Scary Door')
